package com.example.day33_camera2;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

/**
 * Description:camera2管理类
 * Created by Gdu on 2021/4/14 23:53.
 */

public class CameraHelper {
    private Context context;
    private Size mPreviewSize;
    private Point previewViewSize;
    private ImageReader mImageReader;
    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;
    private CameraDevice mCameraDevice;
    private TextureView mTextureView;
    CaptureRequest.Builder mPreviewRequestBuilder;
    private CameraCaptureSession mCaptureSession;
    private Camera2Listener camera2Listener;

    public CameraHelper(Context context) {
        this.context = context;
        camera2Listener = (Camera2Listener) context;
    }

    public synchronized void start(TextureView textureView) {
        mTextureView = textureView;
        //摄像头的管理类
        CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
        try {
            //摄像头的配置信息  0代表后置摄像头
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics("0");
//            以及获取图片输出的尺寸和预览画面输出的尺寸
// 支持哪些格式               获取到的  摄像预览尺寸    textView
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

            mPreviewSize = getBestSupportedSize(new ArrayList<Size>(Arrays.asList(map.getOutputSizes(SurfaceTexture.class))));

            //maxImages 最大输出口
            mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),
                    mPreviewSize.getHeight(),
                    ImageFormat.YUV_420_888, 2);

            //设置后台handler
            mBackgroundThread = new HandlerThread("CameraBackground");
            mBackgroundThread.start();
            mBackgroundHandler = new Handler(mBackgroundThread.getLooper());

            mImageReader.setOnImageAvailableListener(new OnImageAvailableListenerImpl(), mBackgroundHandler);

            //检查摄像机权限
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            cameraManager.openCamera("0", mDeviceStateCallback, mBackgroundHandler);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //region 相机状态回调
    private CameraDevice.StateCallback mDeviceStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            cameraDevice.close();
            mCameraDevice = null;
        }
    };
    //endregion

    //region 建立摄像头会话
    private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture =  mTextureView.getSurfaceTexture();
//            设置预览宽高
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//            创建有一个Surface   画面  ---》1

            Surface surface = new Surface(texture);
            // 创建预览需要的CaptureRequest.Builder
            mPreviewRequestBuilder=  mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

//
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 将SurfaceView的预览的TexetureView作为CaptureRequest.Builder的目标
            mPreviewRequestBuilder.addTarget(surface);

//必须设置  不然  文件   -----》
            mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
            //            保存摄像头   数据  ---H264码流
//  各种回调了
//建立 链接     目的  几路 数据出口
            // 创建CameraCaptureSession，该对象负责管理处理预览请求和拍照请求
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),mCaptureStateCallback,mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    //endregion


    private CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {

        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
//            系统的相机
            // The camera is already closed
            if (null == mCameraDevice) {
                return;
            }
            // 当摄像头已经准备好时，开始显示预览
            mCaptureSession = session;
            try {
                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
                        new CameraCaptureSession.CaptureCallback() {
                        }, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
        }
    };

    //region 拍照得到的临时文件
    private class OnImageAvailableListenerImpl implements ImageReader.OnImageAvailableListener {
        private byte[] y;
        private byte[] u;
        private byte[] v;

        //        摄像 回调应用层  onPreviewFrame(byte[] )  这里 拿哪里
        @Override
        public void onImageAvailable(ImageReader reader) {
            //获取数据  读取下一帧
            Image image= reader.acquireNextImage();
            //将image 内容转换成 H264码流
            Image.Plane[] planes =  image.getPlanes();
            // 重复使用同一批byte数组，减少gc频率
            //yuv420就是nv12
            if (y == null) {//进来一次
//                limit  是 缓冲区 所有的大小     position 起始大小  相减就是长度
                y = new byte[planes[0].getBuffer().limit() - planes[0].getBuffer().position()];//y的长度
                u = new byte[planes[1].getBuffer().limit() - planes[1].getBuffer().position()];//u的长度
                v = new byte[planes[2].getBuffer().limit() - planes[2].getBuffer().position()];//v的长度
            }
            if (image.getPlanes()[0].getBuffer().remaining() == y.length) {
//                分别填到 yuv
                planes[0].getBuffer().get(y);
                planes[1].getBuffer().get(u);
                planes[2].getBuffer().get(v);
            }
            if(camera2Listener!=null){
                camera2Listener.onPreview(y, u, v, mPreviewSize, planes[0].getRowStride());
            }
            //释放掉,使其进入循环   等同于camera的mCamera.addCallbackBuffer(bytes)
            image.close();
        }
    }
    //endregion

    //region 回调,将yuv数据转化成yuv420
    public interface Camera2Listener {
        /**
         * 预览数据回调
         * @param y 预览数据，Y分量
         * @param u 预览数据，U分量
         * @param v 预览数据，V分量
         * @param previewSize  预览尺寸
         * @param stride    步长
         */
        void onPreview(byte[] y, byte[] u, byte[] v, Size previewSize, int stride);
    }
    //endregion

    //region 获取摄像头的最佳尺寸
    private Size getBestSupportedSize(List<Size> sizes) {
        Point maxPreviewSize = new Point(1920, 1080);
        Point minPreviewSize = new Point(1280, 720);
        Size defaultSize = sizes.get(0);
        Size[] tempSizes = sizes.toArray(new Size[0]);
        Arrays.sort(tempSizes, new Comparator<Size>() {
            @Override
            public int compare(Size o1, Size o2) {
                if (o1.getWidth() > o2.getWidth()) {
                    return -1;
                } else if (o1.getWidth() == o2.getWidth()) {
                    return o1.getHeight() > o2.getHeight() ? -1 : 1;
                } else {
                    return 1;
                }
            }
        });
        sizes = new ArrayList<>(Arrays.asList(tempSizes));
        for (int i = sizes.size() - 1; i >= 0; i--) {
            if (maxPreviewSize != null) {
                if (sizes.get(i).getWidth() > maxPreviewSize.x || sizes.get(i).getHeight() > maxPreviewSize.y) {
                    sizes.remove(i);
                    continue;
                }
            }
            if (minPreviewSize != null) {
                if (sizes.get(i).getWidth() < minPreviewSize.x || sizes.get(i).getHeight() < minPreviewSize.y) {
                    sizes.remove(i);
                }
            }
        }
        if (sizes.size() == 0) {
            return defaultSize;
        }
        Size bestSize = sizes.get(0);
        float previewViewRatio;
        if (previewViewSize != null) {
            previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y;
        } else {
            previewViewRatio = (float) bestSize.getWidth() / (float) bestSize.getHeight();
        }

        if (previewViewRatio > 1) {
            previewViewRatio = 1 / previewViewRatio;
        }

        for (Size s : sizes) {
            if (Math.abs((s.getHeight() / (float) s.getWidth()) - previewViewRatio) < Math.abs(bestSize.getHeight() / (float) bestSize.getWidth() - previewViewRatio)) {
                bestSize = s;
            }
        }
        return bestSize;
    }
    //endregion

}
